/*
 * Copyright (C) 2017-2019 Alibaba Group Holding Limited
 */

/******************************************************************************
 * @file     camera_ov_common.c
 * @brief    ov common functions
 * @version  V1
 * @date     11. Nov 2019
 ******************************************************************************/

/* LOG_LEVEL: 0: Err; 1: Err&Warn; 2: Err&Warn&Info; 3: Err&Warn&Info&Debug */
#define LOG_LEVEL 0
#include <syslog.h>

#include <stdio.h>
#include <string.h>
// #include <csi_config.h>
#include <soc.h>
#include <io.h>
#include <pin.h>
#include <drv_usi_iic.h>
#include <drv_camera.h>
#include <csky_common.h>
#include "camera_ov_common.h"

extern void mdelay(uint32_t ms);

ov_priv_t camera_handle[CONFIG_CAMERA_NUM];

const struct regval_list sensor_oe_disable_regs[3] = {
    {0x3000,0x00},
    {0x3001,0x00},
    {0x3002,0x00},
};

const struct regval_list sensor_oe_enable_regs[3] = {
    {0x3000,0x0f},
    {0x3001,0xff},
    {0x3002,0xe4},
};

/* Use iic to write data to camera register */
int32_t ov_write(ov_commu_info_t *commu_info, uint16_t reg, uint8_t val)
{
    int32_t ret;
    uint8_t data[3] = { reg >> 8, reg & 0xff, val};
    bool xfer_pending = false;
    commu_info->cb_transfer_flag = 0;
    ret = csi_iic_master_send(commu_info->iic_handle, commu_info->slave_addr,
                              data, 3, xfer_pending);
    if (ret < 0) {
        LOG_E("csi_iic_master_send error\n");
        return -1;
    }
    while(!commu_info->cb_transfer_flag);

    return 0;
}

/* Use iic to write data list to camera register list */
int32_t ov_write_array(ov_commu_info_t *commu_info, struct regval_list *regs, uint32_t array_size)
{
    int32_t i = 0, ret;
    if(!regs) {
        LOG_E("regs is NULL\n");
        return -EINVAL;
    }

    while(i < array_size) {
        ret = ov_write(commu_info, regs->addr, regs->data);
        if (ret < 0)
            return -1;
        i++;
        regs++;
    }

    return 0;
}

/* Use iic to read data from camera register */
int32_t ov_read(ov_commu_info_t *commu_info, uint16_t reg, uint8_t *read_data)
{
    int32_t ret;
    uint8_t data_w[2] = { reg >> 8, reg & 0xff };
    bool xfer_pending = false;
    ret = csi_iic_master_send(commu_info->iic_handle, commu_info->slave_addr,
                              data_w, 2, xfer_pending);
    if (ret < 0) {
        LOG_E("Send error.\n");
        return -1;
    }
    /* wait for camera ready, reference ov5647_ommivision 2.5.1 */
    mdelay(5);
    commu_info->cb_transfer_flag = 0;
    ret = csi_iic_master_receive(commu_info->iic_handle, commu_info->slave_addr,
                                 read_data, 1, xfer_pending);
    if (ret < 0) {
        LOG_E("receive error.\n");
        return -1;
    }
    while(!commu_info->cb_transfer_flag);
    return 0;
}

int32_t ov_check_id(ov_commu_info_t *commu_info, uint8_t chipid_high, uint8_t chipid_low)
{
    int32_t ret;
    uint8_t data1 = 0, data2 = 0;

    ret = ov_read(commu_info, OV_REG_CHIPID_H, &data1);
    if (ret < 0 || data1 != chipid_high) {
        LOG_E("ov_read error, ret=%d, OV_REG_CHIPID_H=%02x\n", ret, data1);
        return -1;
    }

    ret = ov_read(commu_info, OV_REG_CHIPID_L, &data2);
    if (ret < 0 || data2 != chipid_low) {
        LOG_E("ov_read error, ret=%d, OV_REG_CHIPID_L=%02x\n", ret, data2);
        return -1;
    }

    LOG_I("ChipID=ov%02x%02x\n", data1, data2);
    return 0;
}

int32_t ov_set_stby(ov_commu_info_t *commu_info, int on_off)
{
    int32_t ret;
    uint8_t rdval;
    ret = ov_read(commu_info, 0x0100, &rdval);
    if (ret != 0) {
        LOG_E("ov_read failed, ret=%d\n", ret);
        return ret;
    }
    if (on_off == CSI_STBY_ON)
        ret = ov_write(commu_info, 0x0100, rdval & 0xfe);
    else
        ret = ov_write(commu_info, 0x0100, rdval | 0x01);

    mdelay(5);
    return ret;
}

int32_t ov_stream_on(ov_commu_info_t *commu_info)
{
    /* open all frame number */
    LOG_D("Stream on");
    if (ov_write(commu_info, 0x4202, 0x00) == 0 &&
        ov_write(commu_info, 0x300D, 0x00) == 0) {
        /* wait for camera ready, reference ov5647_ommivision 2.5.1 */
        mdelay(5);
        return 0;
    }
    return -1;
}

int32_t ov_stream_off(ov_commu_info_t *commu_info)
{
    /* close all frame number */
    LOG_D("Stream off");
    if (ov_write(commu_info, 0x4202, 0x0f) == 0 &&
        ov_write(commu_info, 0x300D, 0x01) == 0) {
        mdelay(5);
        return 0;
    }
    return -1;
}

